﻿package com.moonshineproject.plugin.globalerrorhandling {
	import com.moonshineproject.IDEModel;
	import com.moonshineproject.plugin.PluginBase;
	import com.moonshineproject.utils.Pastebin;
	import com.moonshineproject.utils.PastebinEvent;
	import com.moonshineproject.vo.Settings;
	import flash.desktop.Clipboard;
	import flash.desktop.ClipboardFormats;
	import flash.desktop.NotificationType;
	import flash.display.DisplayObject;
	import flash.display.NativeWindow;
	import flash.display.NativeWindowInitOptions;
	import flash.display.NativeWindowSystemChrome;
	import flash.display.NativeWindowType;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.errors.IllegalOperationError;
	import flash.errors.StackOverflowError;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.events.UncaughtErrorEvent;
	import flash.net.navigateToURL;
	import flash.net.URLRequest;
	import flash.net.URLVariables;
	import flash.utils.setTimeout;
	import flash.utils.Timer;
	
	public class GlobalErrorHandlingPlugin extends PluginBase 
	{
		override public function get name():String			{ return "Global Error Handling Plugin"; }
		override public function get author():String		{ return "Moonshine Project Team"; }
		override public function get description():String	{ return "Handles uncaught errors and displays them in a pretty box."; }
		
		private static const BUG_REPORT_URL:String = "http://code.google.com/p/moonshineproject/issues/entry?";
		
		private static const DEFAULT_WIDTH:Number = 800;
		private static const DEFAULT_HEIGHT:Number = 600;
		
		private var display:GEHDisplay;
		private var window:NativeWindow;
		private var windowOptions:NativeWindowInitOptions;
		
		private var dismissAll:Boolean = false;
		
		public function GlobalErrorHandlingPlugin() 
		{
			windowOptions = new NativeWindowInitOptions();
			windowOptions.maximizable = true;
			windowOptions.minimizable = true;
			windowOptions.resizable = true;
			windowOptions.systemChrome = NativeWindowSystemChrome.STANDARD;
			windowOptions.transparent = false;
			windowOptions.type = NativeWindowType.NORMAL;
		}
		
		override public function activate():void 
		{
			super.activate();
			
			var mv:DisplayObject = IDEModel.getInstance().mainView;
			
			if (!mv.stage) 
			{
				mv.addEventListener(Event.ADDED_TO_STAGE, activateStage);
			} else 
			{			
				mv.loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtError);
			}
		}
		
		override public function deactivate():void 
		{
			super.deactivate();
			
			IDEModel.getInstance().mainView.loaderInfo.uncaughtErrorEvents.removeEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtError);
		}
		
		private function activateStage(e:Event):void 
		{
			var mv:DisplayObject = e.target as DisplayObject;
			mv.removeEventListener(Event.ADDED_TO_STAGE, activateStage);
			mv.loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtError);
		}
		
		private function uncaughtError(e:UncaughtErrorEvent):void 
		{
			e.preventDefault();
			
			if (dismissAll) return;
			
			var errorName:String = "";
			var errorStr:String = "";
			
			if (e.error is Error) {
				var error:Error = e.error as Error;
				errorName = error.name+" - "+error.message;
				errorStr = error.getStackTrace();
			} else {
				errorName = errorStr = ""+e.error;
			}
			
			showDisplay();
			
			display.addError(errorName, errorStr);
		}
		
		private function showDisplay():void 
		{
			if (!display) 
			{
				display = new GEHDisplay();
				display.addEventListener(GEHDisplay.CONTINUE, continueAction);
				display.addEventListener(GEHDisplay.DISMISS_ALL, dismissAllAction);
				display.addEventListener(GEHDisplay.BUG_REPORT, bugReportAction);
				display.addEventListener(GEHDisplay.PASTEBIN, pastebinAction);
			}
			if (!window) 
			{
				window = new NativeWindow(windowOptions);
				window.title = "Moonshine: Internal Runtime Errors";
				window.stage.scaleMode = StageScaleMode.NO_SCALE;
				window.stage.align = StageAlign.TOP_LEFT;
				window.stage.addChild(display);
				window.width = DEFAULT_WIDTH;
				window.height = DEFAULT_HEIGHT;
				window.activate();
				window.addEventListener(Event.CLOSE, windowClosed);
				window.x = 0;
				window.y = 0;
			}
			if (!window.active) window.notifyUser(NotificationType.INFORMATIONAL);
		}
		
		private function windowClosed(e:Event):void 
		{
			window = null;
		}
		
		private function continueAction(e:Event):void 
		{
			if (window) 
			{
				window.stage.removeChild(display);
				window.close();
			}
		}
		
		private function dismissAllAction(e:Event):void 
		{
			dismissAll = true;
			if (window) 
			{
				window.stage.removeChild(display);
				window.close();
			}
		}
		
		private function bugReportAction(e:Event):void 
		{
			var title:String = display.getSelectedErrorTitle();
			var text:String = display.getSelectedErrorText();
			
			var url:String = BUG_REPORT_URL;
			
			var vars:URLVariables = new URLVariables();
			
			var labels:Vector.<String> = Vector.<String>(["Type-Defect", "Priority-Medium"]);
			switch (Settings.os) 
			{
				case "win": labels.push("OpSys-Windows"); break;
				case "osx": labels.push("OpSys-OSX"); break;
				case "lin": labels.push("OpSys-Linux"); break;
			}
			labels.push("BR");
			vars["summary"] = "Bug report - "+title;
			vars["labels"] = labels.join(",");
			
			// Trims file paths in stack trace output
			text = text.replace(/(at .*\[).*(?:[\\\/]ide[\\\/]src[\\\/]|[\\\/]framework[\\\/]src[\\\/])(.*?\])/, "$1$2");
			
			// Ensures that the comment is on the end for trimming purposes
			var commentVar:URLVariables = new URLVariables();
			commentVar["comment"] = text;
			
			url += vars+"&"+commentVar;
			
			// Trims the url, so it doesn't exceed a safe limit
			url = url.substr(0, 2000);
			
			// Removes possible broken urlencoded characters on the end, left over from earlier url trimming
			url = url.replace(/%[A-Z0-9]?$/, "");
			
			navigateToURL(new URLRequest(url));
		}
		
		private function pastebinAction(e:Event):void 
		{
			var title:String = display.getSelectedErrorTitle();
			var text:String = display.getSelectedErrorText();
			
			new Pastebin(text, title).addEventListener(PastebinEvent.SUCCESS, pastebinSuccess, false, 0, true);
		}
		
		private function pastebinSuccess(e:PastebinEvent):void 
		{
			var url:String = e.message;
			print("Pastebin url copied to clipboard: "+url);
		}
	}
}